<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>应用管理</title>
    <link rel="stylesheet" href="css/style.css">
    <!-- Vue 3 & Element Plus -->
    <script src="./public/vue.global.prod.js"></script>
    <!-- 引入 Element Plus -->
    <link rel="stylesheet" href="./public/element-plus-index.css">
    <script src="./public/index.full.js"></script>
    <link rel="stylesheet" href="./public/icon.css" />
    <!-- ECharts -->
    <script src="./public/echarts.min.js"></script>
    <script src="./public/axios.min.js"></script>
</head>

<body>
<div id="app">
    <div class="container">
        <div style="color:#888;margin-bottom:16px;font-size: 14px">首页 / 应用管理</div>
        <div class="card">
            <el-form :inline="true" @submit.prevent="query" :model="filters">
                <el-form-item label="应用名称">
                    <el-input v-model="filters.name" placeholder="请选择" style="width:120px;"></el-input>
                </el-form-item>
                <el-form-item label="应用Id">
                    <el-input v-model="filters.id" placeholder="请输入应用Id" style="width:140px;"></el-input>
                </el-form-item>
                <!-- <el-form-item label="状态">
                  <el-select v-model="filters.status" placeholder="全部" style="width:80px;">
                    <el-option label="全部" value=""></el-option>
                    <el-option label="上架" value="上架"></el-option>
                    <el-option label="下架" value="下架"></el-option>
                  </el-select>
                </el-form-item> -->
                <!-- <el-form-item label="渲染类型">
                  <el-select v-model="filters.type" placeholder="全部" style="width:100px;">
                    <el-option label="全部" value=""></el-option>
                    <el-option label="SR(共享-平行云通用方案)" value="SR(共享-平行云通用方案)"></el-option>
                  </el-select>
                </el-form-item> -->
                <el-form-item>
                    <el-button type="primary" native-type="submit">查询</el-button>
                </el-form-item>
            </el-form>
            <el-button type="primary" style="margin-top:16px;" @click="openUploadDialog">添加应用</el-button>
            <!-- 上传弹窗 -->
            <el-dialog v-model="uploadDialogVisible" title="上传应用文件" width="400px">
                <input type="file" ref="fileInput" @change="handleFileChange" />
                <div style="margin-top: 16px; text-align: right;">
                    <el-button @click="uploadDialogVisible = false">取消</el-button>
                    <el-button type="primary" @click="uploadFile" :loading="uploading">上传</el-button>
                </div>
            </el-dialog>
        </div>
        <div class="card">
            <el-table :data="pagedApps" style="width: 100%">
                <el-table-column prop="id" label="应用Id"></el-table-column>
                <el-table-column prop="name" label="应用名称"></el-table-column>
                <el-table-column prop="fileSize" label="文件大小"></el-table-column>
                <el-table-column prop="maxClient" label="最大并发客户端"></el-table-column>
                <el-table-column label="操作" width="300">
                    <template #default="scope">
                      <el-button size="small" type="primary" @click="buildImage(scope.row)" :loading="scope.row.building" :disabled="scope.row.building || scope.row.built">上架</el-button>
                        <el-button size="small" type="success" :disabled="!scope.row.canCreateContainer"
                                   @click="createContainer(scope.row)">启动</el-button>
                        <el-button size="small" type="danger" @click="deleteApp(scope.row)">删除</el-button>
                        <el-dropdown>
                            <el-button size="small">
                                更多
                                <i class="el-icon-arrow-down el-icon--right"></i>
                            </el-button>
                            <template #dropdown>
                                <el-dropdown-menu>
                                    <el-dropdown-item @click="editApp(scope.row)">编辑</el-dropdown-item>
                                    <el-dropdown-item @click="toggleStatus(scope.row)">
                                        {{ scope.row.status === '上架' ? '下架' : '上架' }}
                                    </el-dropdown-item>
                                    <el-dropdown-item @click="more(scope.row)">更多</el-dropdown-item>
                                </el-dropdown-menu>

                            </template>
                        </el-dropdown>
                    </template>
                </el-table-column>
            </el-table>
            <div style="margin-top: 18px; text-align: right;">
                <el-pagination background layout="total, sizes, prev, pager, next" :total="filteredApps.length"
                               :page-size="pageSize" :page-sizes="[5, 10, 20, 50]" :current-page="page" @current-change="handlePageChange"
                               @size-change="handleSizeChange" size="small" hide-on-single-page="true" />
            </div>
        </div>
        <el-dialog v-model="editDialogVisible" title="编辑应用" width="500px">
            <el-form :model="editForm" label-width="100px">
                <el-form-item label="应用Id">
                    <el-input v-model="editForm.id" disabled></el-input>
                </el-form-item>
                <el-form-item label="应用名称">
                    <el-input v-model="editForm.name" disabled></el-input>
                </el-form-item>
                <el-form-item label="文件大小">
                    <el-input v-model="editForm.fileSize" disabled></el-input>
                </el-form-item>
                <el-form-item label="最大并发客户端">
                    <el-input v-model="editForm.maxClient" type="number"></el-input>
                </el-form-item>
            </el-form>
            <div style="text-align: right;">
                <el-button @click="editDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="submitEdit">确定</el-button>
            </div>
        </el-dialog>
    </div>
</div>
<script>
    // 全局token获取函数
    function getToken () {
        return sessionStorage.getItem('token')
    }
    const { createApp, ref, computed, onMounted } = Vue
    const { ElMessage } = ElementPlus
    createApp({
        setup () {
            // 页面加载时获取应用列表
            onMounted(() => {
                fetchApps()
            })
            const filters = ref({
                name: '',
                id: '',
                status: '上架',
                type: ''
            })
            const apps = ref([])
            const editDialogVisible = ref(false)
            const editForm = ref({
                id: '',
                name: '',
                fileSize: '',
                maxClient: 1
            })
            // 获取应用列表
            async function fetchApps () {
                try {
                    const res = await fetch('/app', {
                        method: 'GET',
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': getToken()
                        }
                    })
                    const data = await res.json()
                    if (data.status === 'OK' && Array.isArray(data.entities)) {
                        apps.value = data.entities.map(item => ({
                            id: item.appId,
                            name: item.appName,
                            fileSize: item.fileSize,
                            maxClient: item.maxInstance,
                            fileId: item.fileId,
                            fileName: item.fileName,
                            filePath: item.filePath,
                            canCreateContainer: false,
                            building: false, // 是否正在上架
                            built: false     // 是否已上架成功
                        }))
                    } else {
                        ElMessage.error('获取应用列表失败')
                    }
                } catch (e) {
                    ElMessage.error('请求失败，请检查后端服务')
                }
            }
            const filteredApps = computed(() => {
                return apps.value.filter(app => {
                    return (!filters.value.name || app.name.includes(filters.value.name)) &&
                        (!filters.value.id || app.id.includes(filters.value.id))
                    //  &&
                    // (!filters.value.status || app.status === filters.value.status) &&
                    // (!filters.value.type || app.type === filters.value.type)
                })
            })
            // 分页
            const page = ref(1)
            const pageSize = ref(10)
            const pagedApps = computed(() => {
                const start = (page.value - 1) * pageSize.value
                return filteredApps.value.slice(start, start + pageSize.value)
            })
            function handlePageChange (val) {
                page.value = val
            }
            function handleSizeChange (val) {
                pageSize.value = val
                page.value = 1
            }
            function query () {
                fetchApps()
            }

            // 上传相关
            const uploadDialogVisible = ref(false)
            const uploading = ref(false)
            const selectedFile = ref(null)
            const fileInput = ref(null)
            function openUploadDialog () {
                uploadDialogVisible.value = true
            }
            function handleFileChange (e) {
                selectedFile.value = e.target.files[0]
            }
            //上传
            async function uploadFile () {
                if (!selectedFile.value) {
                    ElMessage.warning('请选择文件')
                    return
                }
                uploading.value = true
                try {
                    const file = selectedFile.value
                    const CHUNK_SIZE = 2 * 1024 * 1024
                    const totalChunks = Math.ceil(file.size / CHUNK_SIZE)
                    // 生成 fileId
                    const fileId = Date.now().toString()
                    let lastResponse = null
                    for (let chunkIdx = 0; chunkIdx < totalChunks; chunkIdx++) {
                        const start = chunkIdx * CHUNK_SIZE
                        const end = Math.min(start + CHUNK_SIZE, file.size)
                        const chunk = file.slice(start, end)
                        const formData = new FormData()
                        formData.append('file', chunk, file.name)
                        formData.append('id', fileId)
                        formData.append('chunk', chunkIdx)
                        formData.append('chunks', totalChunks)
                        formData.append('name', file.name)
                        formData.append('size', file.size)
                        const res = await fetch('/app/upload',
                            {
                                method: 'POST',
                                headers: {
                                    // 'Content-Type': 'application/json',
                                    'Authorization': getToken()
                                },
                                body: formData
                            })

                        lastResponse = await res.json()
                    }
                    if (lastResponse && lastResponse.status === 'OK') {
                        const entity = lastResponse.entity
                        // 新增应用
                        const appPayload = {
                            appName: entity.fileOriginalName,
                            fileId: entity.fileId,
                            fileSize: entity.fileSize,
                            maxInstance: 1
                        }
                        const appRes = await fetch('/app', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                                'Authorization': getToken()
                            },
                            body: JSON.stringify(appPayload)
                        })
                        const appData = await appRes.json()
                        if (appData.status === 'OK') {
                            ElMessage.success('应用添加成功: ' + appData.entity.appName)
                            fetchApps()
                            uploadDialogVisible.value = false
                            selectedFile.value = null
                            if (fileInput.value) fileInput.value.value = ''
                        } else {
                            ElMessage.error('应用添加失败')
                        }
                    } else {
                        ElMessage.error('上传失败')
                    }
                } catch (e) {
                    ElMessage.error('上传失败')
                } finally {
                    uploading.value = false
                }
            }

            // 上架
            async function buildImage (app) {
                try {
                  app.building = true; // 开始加载
                    const payload = {
                        fileId: app.fileId,
                        fileName: app.fileName,
                        filePath: app.filePath,
                        fileOriginalName: app.name
                    }
                    const res = await axios.post('/docker/build', payload, {
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': getToken()
                        }
                    })
                    if (res.data.status === 'OK' && res.data.entity === true) {
                        app.canCreateContainer = true
                        app.built = true
                        app.building = false;
                        ElMessage.success('上架成功')
                    } else {
                        ElMessage.error('镜像构建失败')
                    }
                } catch {
                    ElMessage.error('请求失败，请检查后端服务')
                }finally {
                    app.building = false // 失败时恢复可点，成功时已 built=true
                }
            }

            // 启动
            async function createContainer (app) {
                if (!app.canCreateContainer) return
                try {
                    const payload = {
                        fileId: app.fileId,
                        fileName: app.fileName,
                        filePath: app.filePath,
                        fileOriginalName: app.name
                    }
                    const res = await axios.post('/docker/run', payload, {
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': getToken()
                        }
                    })
                    if (res.data.status === 'OK' && res.data.entity === true) {
                        ElMessage.success('容器启动成功')
                        window.open('rtcPlayer.html')
                    } else {
                        ElMessage.error('容器启动失败')
                    }
                } catch {
                    ElMessage.error('请求失败，请检查后端服务')
                }
            }

            // 删除
            async function deleteApp (app) {
                try {
                    // 二次确认
                    if (!window.confirm(`确定要删除应用 "${app.name}" 吗？`)) return
                    const res = await fetch(`/app/${app.id}`, {
                        method: 'DELETE',
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': getToken()
                        }
                    })
                    const data = await res.json()
                    if (data.status === 'OK' && data.entity === true) {
                        ElMessage.success('删除成功')
                        fetchApps()
                    } else {
                        ElMessage.error('删除失败')
                    }
                } catch (e) {
                    ElMessage.error('请求失败，请检查后端服务')
                }
            }


            // 其他操作
            //form表单
            function editApp (app) {
                editForm.value = {
                    id: app.id,
                    name: app.name,
                    fileSize: app.fileSize,
                    maxClient: app.maxClient
                }
                editDialogVisible.value = true
            }
            // 表单提交
            async function submitEdit () {
                try {
                    const payload = {
                        maxInstance: editForm.value.maxClient
                    }
                    const res = await fetch(`/app/${editForm.value.id}`, {
                        method: 'PUT',
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': getToken()
                        },
                        body: JSON.stringify(payload)
                    })
                    const data = await res.json()
                    if (data.status === 'OK') {
                        ElMessage.success('修改成功')
                        editDialogVisible.value = false
                        fetchApps()
                    } else {
                        ElMessage.error('修改失败')
                    }
                } catch (e) {
                    ElMessage.error('请求失败，请检查后端服务')
                }
            }
            function toggleStatus (app) {
                app.status = app.status === '上架' ? '下架' : '上架'
            }
            function more (app) {
                ElMessage.info('更多操作: ' + app.name)
            }

            return {
                filters,
                apps,
                filteredApps,
                query,
                openUploadDialog,
                uploadDialogVisible,
                uploading,
                selectedFile,
                fileInput,
                handleFileChange,
                uploadFile,
                editApp,
                toggleStatus,
                more,
                page,
                pageSize,
                pagedApps,
                handlePageChange,
                handleSizeChange,
                buildImage,
                fetchApps,
                deleteApp,
                createContainer,
                editDialogVisible,
                editForm,
                submitEdit,
            }
        }
    }).use(ElementPlus).mount('#app');
</script>
</body>

</html>